package tech.horgee.service;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tech.horgee.common.MyCode;
import tech.horgee.dto.MenuListTreeDTO;
import tech.horgee.entity.Menu;
import tech.horgee.entity.RoleMenu;
import tech.horgee.mapper.MenuMapper;
import tech.horgee.mapper.RoleMenuMapper;
import tech.horgee.utils.AssertUtil;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 菜单信息 服务类
 *
 * @author haojun
 * @since 2022-06-12
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class MenuService {
    private final MenuMapper mapper;
    private final RoleMenuMapper roleMenuMapper;

    /**
     * 分页查询列表
     *
     * @param page   分页对象
     * @param entity 查询实体
     * @return 所有数据
     */
    public Page<Menu> page(Page<Menu> page, Menu entity) {
        return mapper.selectPage(page, entity.wrapper());
    }

    /**
     * 不分页查询列表
     *
     * @param entity 实体对象
     * @return 查询结果
     */
    public List<Menu> list(Menu entity) {
        return mapper.selectList(entity.wrapper());
    }

    /**
     * 通过主键查询单条数据
     *
     * @param id 主键
     * @return 单条数据
     */
    public Menu getById(Long id) {
        return mapper.selectById(id);
    }

    /**
     * 新增单条数据
     *
     * @param entity 实体对象
     */
    @Transactional(rollbackFor = Exception.class)
    public void save(Menu entity) {
        checkPermissionUnique(entity);
        mapper.insert(entity);
    }

    /**
     * 修改单条数据
     *
     * @param entity 实体对象
     */
    @Transactional(rollbackFor = Exception.class)
    public void update(Menu entity) {
        checkPermissionUnique(entity);
        AssertUtil.onlyOne(mapper.updateById(entity));
    }

    /**
     * 批量删除数据
     *
     * @param ids 主键集合
     */
    @Transactional(rollbackFor = Exception.class)
    public void deleteByIds(Set<Long> ids) {
        boolean exists = roleMenuMapper.exists(new LambdaQueryWrapper<RoleMenu>().in(RoleMenu::getMenuId, ids));
        if (exists) throw MyCode.DATA_VERIFY_ERROR.toException("权限已绑定角色，无法删除");
        AssertUtil.updated(mapper.deleteBatchIds(ids));
    }

    /**
     * 校验权限是否唯一
     *
     * @param menu 权限信息
     */
    public void checkPermissionUnique(Menu menu) {
        Long menuId = Objects.isNull(menu.getMenuId()) ? Long.valueOf(-1L) : menu.getMenuId();
        Menu info = mapper.checkPermissionUnique(menu.getPermission());
        if (Objects.nonNull(info) && info.getMenuId().longValue() != menuId.longValue()) {
            throw MyCode.DUPLICATE_KEY.toException("权限编码已存在");
        }
    }

    /**
     * 获取权限列表和树结构
     */
    public MenuListTreeDTO getListAndTree() {
        MenuListTreeDTO menuListTreeDTO = new MenuListTreeDTO();
        List<Menu> list = list(new Menu());
        menuListTreeDTO.setList(list);
        menuListTreeDTO.setTree(buildTree(list));
        return menuListTreeDTO;
    }

    private List<Menu> buildTree(List<Menu> list) {
        if (CollUtil.isEmpty(list)) return Collections.emptyList();
        Map<Long, List<Menu>> parentIdMap = list.stream().collect(Collectors.groupingBy(Menu::getParentId));
        Long parentId = Collections.min(parentIdMap.keySet());
        List<Menu> menus = parentIdMap.get(parentId);
        return fillChildren(menus, parentIdMap);
    }

    private List<Menu> fillChildren(List<Menu> list, Map<Long, List<Menu>> parentIdMap) {
        return list.stream().peek(i -> {
            if (parentIdMap.containsKey(i.getMenuId())) {
                i.setChildren(fillChildren(parentIdMap.get(i.getMenuId()), parentIdMap));
            }
        }).collect(Collectors.toList());
    }

}
